//
//  HQBaseRequestSDK.m
//  B2C_iPhone_V4
//
//  Created by JimBo on 15/7/21.
//  Copyright (c) 2015年 edu24ol. All rights reserved.
//

#import "HQBaseRequestSDK.h"
//#import "HQAppSetting.h"
#import "DNSHelper.h"
#import "IPHelper.h"
#import <libkern/OSAtomic.h>
#import <AFNetworkReachabilityManager.h>
#import <ReactiveCocoa/RACEXTScope.h>
#import "Realm.h"
#import "HQBaseRequestSDKSetting.h"

typedef void(^B2CRequestSuccBlock)(YTKBaseRequest *request);
typedef void(^B2CRequestFailBlock)(NSError *error);

NSString * const TokenExpiredNotification = @"TokenExpiredNotification";
static const int maxRetryCount = 3;


static BOOL isRelogining;
static int  retryCount;
static NSMutableArray<HQBaseRequestSDK *> *retryQueue;
static OSSpinLock retryQLock = OS_SPINLOCK_INIT;
static long long apiCacheSec  =   60*24*3600;  //默认60天缓存时间

//static long long currentApiCacheSec  =  -1;///当前api缓存有效期

@interface HQBaseRequestSDK ()

@property (nonatomic, copy) B2CRequestSuccBlock succBlock;
@property (nonatomic, copy) B2CRequestFailBlock failBlock;

@property (nonatomic, assign) int dnsRetryCount;
@property (nonatomic, strong) NSString *originHost;

//@property (nonatomic, assign) NSInteger currentApiCacheSec;//缓存有效期


@end

@implementation HQBaseRequestSDK

+ (void)retry:(HQBaseRequestSDK *)request
{
    OSSpinLockLock(&retryQLock);
    if (!retryQueue) {
         retryQueue = @[].mutableCopy;
    }
    [retryQueue addObject:request];
    OSSpinLockUnlock(&retryQLock);

    if (isRelogining) {
         return;
    }
    
    if (++retryCount > maxRetryCount) {
        [request handleReloginTooMuch];
        DDLogInfo(@"<HQBaseRequest> retry too much time");
        return;
    }
    
    DDLogInfo(@"<%@> relogin and retry later", NSStringFromClass([request class]));
    
    isRelogining = YES;
    
    //暂时直接抛出，不再重试
    [request getTokenBlock:^(BOOL success,NSString * token) {
        if (success && token.length > 0) {
             for (HQBaseRequestSDK* req in retryQueue) {
                 DDLogInfo(@"<%@> relogin succ and resend req for %d time", NSStringFromClass([req class]), retryCount);
                 [req setToken:token];
                 [req startB2CRequestWithCompletionBlockWithSuccess:req.succBlock failure:req.failBlock];
             }
            OSSpinLockLock(&retryQLock);
            [retryQueue removeAllObjects];
            OSSpinLockUnlock(&retryQLock);
            retryCount = 0;
            isRelogining = NO;
        }else{
            [retryQueue.firstObject handleReloginTooMuch];
        }
    }];
    
    
    //暂时屏蔽
//    [Login_CustomMananger autoLoginWithSucc:^(BOOL succecc) {
//        for (HQBaseRequest* req in retryQueue) {
//            DDLogInfo(@"<%@> relogin succ and resend req for %d time", NSStringFromClass([req class]), retryCount);
//            [req setToken:[GVUserDefaults standardUserDefaults].hq_token];
//            [req startB2CRequestWithCompletionBlockWithSuccess:req.succBlock failure:req.failBlock];
//        }
//        OSSpinLockLock(&retryQLock);
//        [retryQueue removeAllObjects];
//        OSSpinLockUnlock(&retryQLock);
//        retryCount = 0;
//        isRelogining = NO;
//    } failure:^(NSError *failure) {
//         [retryQueue.firstObject handleReloginTooMuch];
//        //抛通知登录态过期
//         [HQAppSetting setLoginStatus];
//    }];
    
    
}


- (instancetype)initWithToken:(NSString *)token
{
    if (self = [super init]) {
        if (token) {
             self.token = token;
        }
    }
    return self;
}
 
- (void)getTokenBlock:(void (^)(BOOL success,NSString * token))block
{
    ///子类重写
    if (block) {
        block(NO,@"");
    }
}

- (void)startHQRequestWithCompletionBlockWithSuccess:(NSInteger)lifeSec
                    forceUpdateCache:(BOOL)updateCache
                             success:(void (^)(YTKBaseRequest *request))success
                             failure:(void (^)(NSError *error))failure
{
    @weakify(self)
    void (^successBlock)(HQBaseRequestSDK *request)  = ^(HQBaseRequestSDK *request) {
        @strongify(self)
        
        NSDictionary *json = request.responseJSONObject;
        if (lifeSec > 0 && updateCache) {
            json = request.responseObject;
        }
        
        @try {
            if ([self usingSimpleStatus:json]) {
                //非标准的返回状态留给上层处理
                if (success) {
                    success(request);
                }
                return ;
            }
            
            if (json && [json isKindOfClass:[NSDictionary class]] && json[@"status"]) {
                //获取状态
                HQResponseStatus *status = [HQResponseStatus new];
                if ([json[@"status"] isKindOfClass:[NSDictionary class]]) {
                    status.code = [json[@"status"][@"code"] integerValue];
                    status.tips = json[@"status"][@"tips"];
                    status.msg = json[@"status"][@"msg"];
                    status.sip = json[@"status"][@"sip"];
                    status.time = json[@"status"][@"time"];
                }
                
//                HQResponseStatus *status = [HQResponseStatus mj_objectWithKeyValues:json[@"status"]];
                if (status.cip && status.cip.length > 4 && [IPHelper shareInstance].ip.length == 0) {
                    [IPHelper shareInstance].ip = status.cip;
                }
                //状态判断 如果状态为0，则表示成功
                if ([request isKindOfClass:[@"RetryAPI" class]]) {
                    DDLogInfo(@"<%@>dns retry succ", [request class]);
                }
                
                __block BOOL isSuccess = NO;
                NSSet *successCodes = [self successCodes];
                [successCodes enumerateObjectsUsingBlock:^(id  _Nonnull obj, BOOL * _Nonnull stop) {
                    if (status.code == [obj integerValue]) {
                        isSuccess = YES;
                        *stop = YES;
                    }
                }];
                
                if (isSuccess) {
                    if (success) {
                        success(request);
                    }
                } else if (status.code == TokenExpired) {
                    [self handleTokenExpiredWithSuccess:success failure:failure];
                    
                } else {
                    NSString *msg = status.msg ? status.msg : @"";
                    NSError *error = [NSError errorWithDomain:msg code:status.code userInfo:nil];
                    DDLogInfo(@"<%@> req(%@) error, code is %ld, msg is %@;parameter=%@", [request class], request.requestUrl, status.code, status.msg,request.requestArgument);
                    if (failure) {
                        failure(error);
                    }
                }
            } else {
                NSError *error = [NSError errorWithDomain:@"数据异常" code:request.responseStatusCode userInfo:nil];
                DDLogError(@"<%@> req(%@) Error return data format:147 %@;parameter=%@",[request class], request.requestUrl, json.description,request.requestArgument);
                if (failure) {
                    failure(error);
                }
            }
            DDLogDebug(@"request %@ finish", request);
        } @catch (NSException *exception) {
            DDLogError(@"<%@> exp %@, req(%@), resp(%@),parameter=%@",[request class], exception, request.requestUrl, json.description,request.requestArgument);
            RLMRealm *realm = [RLMRealm defaultRealm];
            if (realm.inWriteTransaction) {
                [realm cancelWriteTransaction];
            }
//            [[HiidoSDK sharedObject] reportFailure:[GVUserDefaults standardUserDefaults].hq_userId actionId:@"API_EXP" actionType:NSStringFromClass([request class]) failCode:@"0" failMessage:exception.description parm:json.description];
        }
        
    };
    
    if ([[DNSHelper shareInstance] shouldUseIpForRequest:self]) {
        [[DNSHelper shareInstance] dnsRetryWithRequest:self success:success failure:^(YTKBaseRequest *request) {
            DDLogError(@"<%@> api failed err,error=%@", [request class],request.error);
            DDLogDebug(@"request url %@ finish", request);
            NSInteger code = request.responseStatusCode ? request.responseStatusCode : request.error.code;
            NSError *erro  = [NSError errorWithDomain:@"com.edu24ol.dnshelper" code:code userInfo:nil];
            if (![AFNetworkReachabilityManager sharedManager].isReachable) {
                erro  = [NSError errorWithDomain:@"网络连接失败啦~" code:code userInfo:nil];
            }
          
            if (failure) {
                failure(erro);
            }
            
        }];
        return;
    }
    
    void (^failBlock)(HQBaseRequestSDK *request) = ^(HQBaseRequestSDK *request) {
        
        [[DNSHelper shareInstance] handleFailRequest:request withSuccess:[successBlock copy] failure:^(YTKBaseRequest *request) {
            DDLogError(@"<%@> api failed err,error=%@,statuscode=%ld", [request class],request.error,request.responseStatusCode);

            NSInteger code = request.responseStatusCode ? request.responseStatusCode : request.error.code;
            NSError *erro  = [NSError errorWithDomain:@"com.edu24ol.dnshelper" code:code userInfo:nil];
            if (![AFNetworkReachabilityManager sharedManager].isReachable) {
                erro  = [NSError errorWithDomain:@"网络连接失败啦~" code:code userInfo:nil];
            }
            if (failure) {
                failure(erro);
            }

        }];
    };
    
    self.currentApiCacheSec = lifeSec;
    
    [self setCompletionBlockWithSuccess:successBlock failure:failBlock];
    if (lifeSec > 0 && updateCache) {
        [super startWithoutCache];
    } else {
        [super start];
    }
    
}

- (void)updateB2CRequestCacheWithLife:(NSInteger)lifeSec
                              success:(void (^)(YTKBaseRequest *request))success
                              failure:(void (^)(NSError *error))failure
{
    [self startHQRequestWithCompletionBlockWithSuccess:lifeSec forceUpdateCache:YES success:success failure:failure];
}

- (void)startB2CRequestUsingCache:(BOOL)usingCache
                          success:(void (^)(YTKBaseRequest *request))success
                          failure:(void (^)(NSError *error))failure
{
    
    [self startHQRequestWithCompletionBlockWithSuccess:(usingCache?apiCacheSec:(-1)) forceUpdateCache:NO success:success failure:failure];
}

- (void)startB2CRequestWithCompletionBlockWithSuccess:(void (^)(YTKBaseRequest *request))success failure:(void (^)(NSError *error))failure
{
    [self startB2CRequestUsingCache:NO success:success failure:failure];
}



/**
 *  兼容旧接口
 *
 *  @param success
 *  @param failure
 */
- (void)startWithCompletionBlockWithSuccess:(YTKRequestCompletionBlock)success failure:(YTKRequestCompletionBlock)failure
{
    __weak typeof(self) weakSelf = self;
    B2CRequestFailBlock fail = ^(NSError *err) {if (failure) {
        failure(weakSelf);
    }};
    
    [self startB2CRequestWithCompletionBlockWithSuccess:success failure:fail];
}

- (void)handleReloginTooMuch
{
    DDLogInfo(@"<HQBaseRequest> relogin fail too much times");
    [[NSNotificationCenter defaultCenter] postNotificationName:TokenExpiredNotification object:self];
    isRelogining = NO;
    retryCount = 0;
    
    OSSpinLockLock(&retryQLock);
    [retryQueue removeAllObjects];
    OSSpinLockUnlock(&retryQLock);
}

- (void)handleTokenExpiredWithSuccess:(void (^)(YTKBaseRequest *request))success failure:(void (^)(NSError *error))failure
{
    NSString * localToken = @"";
    if ([self respondsToSelector:@selector(getCurrentToken_baseReqSDK)]) {
        localToken = [self performSelector:@selector(getCurrentToken_baseReqSDK)];
    }
    if ([self.token isEqualToString:localToken]) {
        DDLogInfo(@"<HQBaseRequest> token expired, insert to relogin waiting queue");
        self.succBlock = success;
        self.failBlock = failure;
        [HQBaseRequestSDK retry:self];
    } else if(localToken.length > 0){
        DDLogInfo(@"<HQBaseRequest> token expired, try a new token");
        [self setToken:localToken];
        [self startB2CRequestWithCompletionBlockWithSuccess:success failure:failure];
    }else{
        if (failure) {
            failure([NSError errorWithDomain:@"token 为空" code:-0 userInfo:nil]);
        }
    }
}

- (NSInteger)cacheTimeInSeconds
{
    return _currentApiCacheSec;
}
 


- (NSSet *)successCodes
{
    return [NSSet setWithObjects:@(0), nil];
}

/*
 以下两类返回没有status详情
 {
    "status": 1,
    "data": { }
 }
 
 {  "code": 1,
    "msg": 99437
 }
 */
- (BOOL)usingSimpleStatus:(NSDictionary *)respDict
{
    id status = respDict[@"status"];
    
    if (status && [status isKindOfClass:[NSDictionary class]]) {
         return NO;
    }
    
    if (!status) {
         status = respDict[@"code"];
    }
    return (status == nil);
}


- (id)cacheFileNameFilterForRequestArgument:(id)argument
{
    return [self getCacheFileNameArgument];
}

- (id)getCacheFileNameArgument
{
    NSDictionary *argumentDic = [self requestArgument];
    NSMutableDictionary *dic = argumentDic.mutableCopy;
    for (NSString * key in [self filterKeyArrayForCacheFileName]) {
        [dic removeObjectForKey:key];
    }
    [dic addEntriesFromDictionary:[self addArgumentForCacheFileName]];
    return dic;
}

- (NSDictionary *)addArgumentForCacheFileName
{
    uint64_t useid = 0;
    if ([self respondsToSelector:@selector(getCurrentUserId_baseReqSdk)]) {
        useid = [self performSelector:@selector(getCurrentUserId_baseReqSdk)];
    }
    
    NSString *cacheMark = [NSString stringWithFormat:@"1%ld3",(long)useid];
    return  @{@"cacheMark":cacheMark};
}

- (NSArray *)filterKeyArrayForCacheFileName
{
    return @[@"edu24ol_token"];
}
- (BOOL)isFirstPageRequest
{
    return NO;
}

- (NSInteger)ErrorCode_HaveNoData
{
    return 30001;
}

@end

